package com.gupao.config;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
public class Auth2ServerConfig {
	
	public final static String OPENAPI_RESOURCE_ID="openapi"; 
	
	
	@Configuration
	@EnableWebSecurity
	public static class WebSecurityConfigaration extends WebSecurityConfigurerAdapter{
		
		@Autowired
		UserDetailsService userDetailsService;
		
		@Autowired
		private OpenapiPasswordEncoder passwordEncoder;
		
		@Autowired
		MobileAuthenticationSuccessHandler mobileAuthenticationSuccessHandler;
		
		@Autowired
		MobileAuthenticationFailureHandler mobileAuthenticationFailureHandler;
		
		@Autowired
		SecurityProperties securityProperties;
		
		@Override
	    public void configure(WebSecurity web) throws Exception {
	        web.ignoring().antMatchers("/WEB-INF/**","/*.jsp", "/images/**", "/oauth/uncache_approvals", "/oauth/cache_approvals");
	    }
		
		@Bean
		@Override
		protected AuthenticationManager authenticationManager() throws Exception {
			AuthenticationManager manager=super.authenticationManager();
			return manager;
		}
		
		@Override
		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
			auth.authenticationProvider(daoAuthenticationProvider());
		}

		@Override
		protected void configure(HttpSecurity http) throws Exception {
			 http
			 .requestMatchers()
             .antMatchers(securityProperties.getBrowser().getLoginPage(),"/auth/authorize", "/authentication/require", "/oauth/authorize","/user/hello","/oauth/confirm_access")
             .and()
             .csrf()
             .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
             .disable()//跨域关闭
             .formLogin()
             .loginPage("/authentication/require")//自定义登录请求
             .loginProcessingUrl("/auth/authorize")//自定义表单登录地址
//             .successHandler(mobileAuthenticationSuccessHandler)
//             .failureHandler(mobileAuthenticationFailureHandler)
             .and()
             .authorizeRequests()
             .antMatchers(securityProperties.getBrowser().getLoginPage(),
                     "/authentication/require","/oauth/authorize","/oauth/confirm_access")//此路径放行 否则会陷入死循环
             .permitAll()
             .anyRequest()
//             .permitAll()
             .authenticated()
             
             ;
			 http.httpBasic().disable();
		}
		
		@Bean
	    public DaoAuthenticationProvider daoAuthenticationProvider(){
	        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
	        // 设置userDetailsService
	        provider.setUserDetailsService(userDetailsService);
	        // 禁止隐藏用户未找到异常
//	        provider.setHideUserNotFoundExceptions(false);
	        // 使用BCrypt进行密码的hash
	        provider.setPasswordEncoder(passwordEncoder);
	        return provider;
	    }
		
		
		
		
		
	}
	
	@Configuration
	@EnableResourceServer
	public static class ResourceServerConfigaration extends ResourceServerConfigurerAdapter{
		@Autowired
		MobileAuthenticationSuccessHandler mobileAuthenticationSuccessHandler;
		
		@Autowired
		MobileAuthenticationFailureHandler mobileAuthenticationFailureHandler;
		
		@Autowired
		SecurityProperties securityProperties;

		@Override
		public void configure(HttpSecurity http) throws Exception {
			http
			// Since we want the protected resources to be accessible in the UI as well we need 
			// session creation to be allowed (it's disabled by default in 2.0.6)
			.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
		.and()
			.requestMatchers().antMatchers("/photos/**", "/oauth/users/**", "/oauth/clients/**","/me")
		.and()
			.authorizeRequests()
				.antMatchers("/me").access("#oauth2.hasScope('read')")					
				.antMatchers("/photos").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")                                        
				.antMatchers("/photos/trusted/**").access("#oauth2.hasScope('trust')")
				.antMatchers("/photos/user/**").access("#oauth2.hasScope('trust')")					
				.antMatchers("/photos/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")
				.regexMatchers(HttpMethod.DELETE, "/oauth/users/([^/].*?)/tokens/.*")
					.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')")
				.regexMatchers(HttpMethod.GET, "/oauth/clients/([^/].*?)/users/.*")
					.access("#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')")
				.regexMatchers(HttpMethod.GET, "/oauth/clients/.*")
					.access("#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')");
		}

		@Override
		public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
			resources.resourceId(OPENAPI_RESOURCE_ID).stateless(false);
		}
		
	}
	
	@Configuration
	@EnableAuthorizationServer
	public static class AuthorizationServerConfigaration extends AuthorizationServerConfigurerAdapter{
		
		@Autowired
		private BossCustomClientDetailsService bossCustomClientDetailsService;
		
		@Autowired
		private OpenapiPasswordEncoder passwordEncoder;
		
		@Autowired
		AuthenticationManager authenticationManager;
		
		@Autowired
		UserDetailsService userDetailsService;
		
		@Autowired
		RedisConnectionFactory redisConnectionFactory;
		
		@Autowired
		private DataSource dataSource;
		
		@Autowired
		private UserApprovalHandler userApprovalHandler;
		
		
		@Override
		public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
			endpoints.authenticationManager(authenticationManager)
			.userApprovalHandler(userApprovalHandler)
			.userDetailsService(userDetailsService)
			.tokenStore(new JdbcTokenStore(dataSource))
//			.reuseRefreshTokens(false)
//			.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource))
			.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
		}
		
		

		@Override
		public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//			security.allowFormAuthenticationForClients().passwordEncoder(passwordEncoder);
			security.realm("mobile");
		}

		@Override
		public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
			clients.withClientDetails(bossCustomClientDetailsService);
		}
		
	}
	
	protected static class Stuff {

		@Autowired
		private ClientDetailsService clientDetailsService;
		
		@Autowired
		private DataSource dataSource;

		@Bean
		public ApprovalStore approvalStore() throws Exception {
			TokenApprovalStore store = new TokenApprovalStore();
			store.setTokenStore(new JdbcTokenStore(dataSource));
			return store;
		}

		@Bean
		@Lazy
		@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
		public ThirdUserApprovalHandler userApprovalHandler() throws Exception {
			ThirdUserApprovalHandler handler = new ThirdUserApprovalHandler();
			handler.setApprovalStore(approvalStore());
			handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
			handler.setClientDetailsService(clientDetailsService);
			handler.setUseApprovalStore(true);
			return handler;
		}
	}
}
